package com.radar.io.cinrad;

import cn.hutool.core.date.DateUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.radar.algorithm.MathCommon;
import com.radar.common.CinradType;
import com.radar.common.DateConstants;
import com.radar.common.ProductType;
import com.radar.common.RadarSite;
import com.radar.exception.RadarDecodeError;
import com.radar.utils.BitConverter;
import ucar.ma2.ArrayByte;
import ucar.ma2.ArrayDouble;
import ucar.ma2.DataType;
import ucar.nc2.Attribute;
import ucar.nc2.Dimension;
import ucar.nc2.Variable;

import java.io.IOException;
import java.util.*;
import java.util.stream.IntStream;

/**
 * @author: fy-wbj
 * @description: 382 波段 雷达
 * @date: 2020/10/10
 */
public class Radar382 extends BaseCinrad {

    private double[] ele;

    private double[][] azi;

    private double reso = 300;

    private Date scanEndTime;

    double maxL;

    static Log log = LogFactory.get();

    double lat;

    double lon;


    public Radar382(String file, CinradType type, String code) {
        super(file, type, code);
    }

    public void parse(byte[] bytes) throws RadarDecodeError {
        try {
            int index = 0;
            byte[] bytdata = new byte[594];
            System.arraycopy(bytes, index, bytdata, 0, bytdata.length);
            index += 594;
            RadarDataFileheader header = new RadarDataFileheader();
            header.parse(bytdata);
            GregorianCalendar gc = new GregorianCalendar();
            String format = header.radarObservationInfo.SYear + "-" + (header.radarObservationInfo.SMonth < 10 ? "0" + header.radarObservationInfo.SMonth : header.radarObservationInfo.SMonth) + "-" +
                    (header.radarObservationInfo.SDay < 10 ? "0" + header.radarObservationInfo.SDay : header.radarObservationInfo.SDay);
            Date date = DateUtil.parse(format, DateConstants.DATE_FORMAT_YY_MM_DD);
            gc.setTime(date);
            gc.set(GregorianCalendar.HOUR_OF_DAY, header.radarObservationInfo.SHour);
            gc.set(GregorianCalendar.MINUTE, header.radarObservationInfo.SMinute);
            gc.set(GregorianCalendar.SECOND, header.radarObservationInfo.SSecond);
            scanEndTime = gc.getTime();
            dateTime = gc.getTime();
            int Layers = header.radarObservationInfo.LayerNum;
            int[] Records = new int[Layers];
            double[] MaxV = new double[Layers];
            int Bins = 0;
            for (int i = 0; i < Layers; i++) {
                Records[i] = header.radarObservationInfo.LayerParam[i].RecodeNumber;
                Bins = header.radarObservationInfo.LayerParam[i].BinNumber;
                reso = header.radarObservationInfo.LayerParam[i].BinWidth / 10;
                MaxV[i] = (double) (int) header.radarObservationInfo.LayerParam[i].MaxV / 100.0;
            }
            double MaxL = header.radarObservationInfo.LayerParam[0].MaxL * 10;
            int max = Arrays.stream(Records).max().getAsInt();
            byte[][][] Z = new byte[Layers][max][Bins];
            byte[][][] V = new byte[Layers][max][Bins];
            byte[][][] W = new byte[Layers][max][Bins];

            ele = new double[Layers];
            azi = new double[Layers][max];
            MathCommon.fill(ele);
            MathCommon.fill(azi);
            String[][] Lat = new String[Layers][max];
            String[][] Lon = new String[Layers][max];
            float[][] Hs = new float[Layers][max];
            float[][] Vs = new float[Layers][max];
            int[][] nv = new int[Layers][max];
            int[][] Course = new int[Layers][max];
            MathCommon.fill(Z);
            MathCommon.fill(V);
            MathCommon.fill(W);
            for (int i = 0; i < Layers; i++) {
                for (int j = 0; j < Records[i]; j++) {
                    bytdata = new byte[4144];
                    System.arraycopy(bytes, index, bytdata, 0, bytdata.length);
                    lineDataBlock data = new lineDataBlock();
                    data.parse(bytdata);
                    index += 4144;
                    ele[i] = (double) data.Elevation / 100.0;
                    azi[i][j] = (double) (int) data.Azimuth / 100.0;
                    Lat[i][j] = data.Latitude;
                    Lon[i][j] = data.Longitude;
                    nv[i][j] = data.Nv;
                    Course[i][j] = data.Course;
                    Vs[i][j] = data.Vs;
                    Hs[i][j] = data.Hs;
                    if (i == 0 && j == 0) {
                        String split[] = data.Longitude.split("/");
                        lon = RadarSite.Dms2D(split[0].replace("E", "").replace("N", "") + "°" + split[1] + "'" + split[2] + "\"");
                        split = data.Latitude.split("/");
                        lat = RadarSite.Dms2D(split[0].replace("E", "").replace("N", "") + "°" + split[1] + "'" + split[2] + "\"");
                    }
                    for (int k = 0; k < Bins; k++) {
                        W[i][j][k] = MathCommon.CodeV(MaxV[i] / 512.0 * (double) (int) data.W[k]);
                        double z = (double) (data.CorZ[k] - 64) / 2.0;
                        Z[i][j][k] = z == -32 ? -128 : MathCommon.CodeRef(z);
                        if (data.V[k] == -128) {
                            V[i][j][k] = MathCommon.CodeV(-128);
                        } else {
                            V[i][j][k] = MathCommon.CodeV(MaxV[i] / 127.0 * (double) data.V[k]);
                        }
                    }
                }
            }
            this.initNetcdfTemp();
            this.writeBasicData(Z, V, W);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("382 handle error");
        }
    }

    @Override
    public double[] getElevation() {
        return ele;
    }

    @Override
    public int[] getElevationIndex(ProductType type) {
        return IntStream.range(0, ele.length).toArray();
    }

    @Override
    public double[][] getAzimuth() {
        return azi;
    }

    @Override
    public double getResolution(ProductType type) {
        return reso;
    }

    @Override
    public RadarSite.RadarSiteItem getRadarSite() throws RadarDecodeError {
        return new RadarSite().new RadarSiteItem("", "", "", "", CinradType.RADAR_382, lon, lat, 0);
    }

    public void writeBasicData(byte[][][] z, byte[][][] v, byte[][][] w) throws IOException {
        int dlength = z[0][0].length;
        write.setRedefineMode(true);
        Variable v1;
        if ((v1 = this.netcdf.findVariable("distince")) == null) {
            write.addDimension(null, "distince", dlength);
            v1 = write.addVariable(null, "distince", DataType.DOUBLE, "distince");
            v1.addAttribute(new Attribute("units", "m"));
            v1.addAttribute(new Attribute("resolution", reso));
        }
        List<Dimension> dimensions = new ArrayList<>();
        dimensions.add(netcdf.findDimension("elevation"));
        dimensions.add(netcdf.findDimension("azimuth"));
        dimensions.add(netcdf.findDimension("distince"));
        Variable rVariable = ProductType.REF.writeVariable(write, netcdf, dimensions);
        Variable vVariable = ProductType.VEL.writeVariable(write, netcdf, dimensions);
        Variable wVariable = ProductType.SW.writeVariable(write, netcdf, dimensions);
        ArrayDouble rDistances = new ArrayDouble.D1(dlength);
        IntStream.range(0, dlength).forEach(i -> rDistances.setDouble(i, (i + 1) * reso));
        ArrayByte rArray = MathCommon.toByteArray(z);
        ArrayByte wArray = MathCommon.toByteArray(w);
        ArrayByte vArray = MathCommon.toByteArray(v);
        rVariable.setCachedData(rArray);
        vVariable.setCachedData(vArray);
        wVariable.setCachedData(wArray);
        v1.setCachedData(rDistances);
    }

    //region 原始数据文件
    public class RadarDataFileheader {
        public RadarPerformanceParamA radarPerformanceInfo;
        public RadarObservationParamA radarObservationInfo;
        public byte[] Reserved;

        public void parse(byte[] b) {
            int index = 0;
            byte[] bytdata = new byte[54];
            System.arraycopy(b, index, bytdata, 0, bytdata.length);
            radarPerformanceInfo = new RadarPerformanceParamA();
            radarPerformanceInfo.parse(bytdata);
            index += 54;

            bytdata = new byte[516];
            System.arraycopy(b, index, bytdata, 0, bytdata.length);
            radarObservationInfo = new RadarObservationParamA();
            radarObservationInfo.parse(bytdata);
            index += 516;
            Reserved = new byte[24];
            System.arraycopy(b, index, Reserved, 0, 24);
            index += 24;
        }

    }

    public class RadarPerformanceParamA {
        public int AntennaG;
        public int BeamH;
        public int BeamL;
        public byte Polarizations;
        public short SideLobe;
        public int Power;
        public int WaveLength;
        public int LineA;
        public short LineMinPower;
        public int ClutterP;
        public int ClutterS;
        public byte VelocityP;
        public byte FilterP;
        public byte FilterS;
        public byte NoiseT;
        public byte intensityR;
        public byte[] Reserved;

        public void parse(byte[] b) {
            int index = 0;
            AntennaG = BitConverter.toInt32(b, index);
            index += 4;
            BeamH = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            BeamL = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            Polarizations = b[index];
            index += 1;
            SideLobe = BitConverter.toInt16(b, index);
            index += 2;
            Power = BitConverter.toInt32(b, index);
            index += 4;
            WaveLength = BitConverter.toInt32(b, index);
            index += 4;
            LineA = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            LineMinPower = BitConverter.toInt16(b, index);
            index += 2;
            ClutterP = (0xff & b[index]);
            index++;
            ClutterS = (0xff & b[index]);
            index++;
            VelocityP = b[index];
            index++;
            FilterP = b[index];
            index++;
            FilterS = b[index];
            index++;
            NoiseT = b[index];
            index++;
            intensityR = b[index];
            index++;
            Reserved = new byte[24];
            System.arraycopy(b, index, Reserved, 0, 24);
        }
    }

    public class RadarObservationParamA {
        public byte SType;
        public byte WeatherMode;
        public int SYear;
        public byte SMonth;
        public byte SDay;
        public byte SHour;
        public byte SMinute;
        public byte SSecond;
        public byte Calibration;
        public byte LayerNum;
        // 19
        public LayerParamA[] LayerParam;
        public int RHIA;
        public short RHIL;
        public short RHIH;
        public byte[] Reserved;

        public void parse(byte[] b) {
            int index = 0;
            SType = b[index];
            index++;
            WeatherMode = b[index];
            index++;
            SYear = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            SMonth = b[index];
            index++;
            SDay = b[index];
            index++;
            SHour = b[index];
            index++;
            SMinute = b[index];
            index++;
            SSecond = b[index];
            index++;
            Calibration = b[index];
            index++;
            LayerNum = b[index];
            index++;
            LayerParam = new LayerParamA[25];
            for (int i = 0; i < LayerParam.length; i++) {
                byte[] bytdata = new byte[19];
                System.arraycopy(b, index, bytdata, 0, bytdata.length);
                LayerParam[i] = new LayerParamA();
                LayerParam[i].parse(bytdata);
                index += 19;
            }
            RHIA = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            RHIL = BitConverter.toInt16(b, index);
            index += 2;
            RHIH = BitConverter.toInt16(b, index);
            index += 2;
            Reserved = new byte[24];
            System.arraycopy(b, index, Reserved, 0, 24);
        }
    }

    public class LayerParamA {
        public byte Ambiguousp;
        public int PRF1;
        public int PRF2;
        public byte Filter;
        public int PluseW;
        public int MaxV;
        public int MaxL;
        public int BinWidth;
        public int BinNumber;
        public int RecodeNumber;
        public int DataForm;

        public void parse(byte[] b) {
            int index = 0;
            Ambiguousp = b[index];
            index++;
            PRF1 = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            PRF2 = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            Filter = b[index];
            index++;
            PluseW = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            MaxV = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            MaxL = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            BinWidth = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            BinNumber = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            RecodeNumber = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            DataForm = (0xff & b[index]);
            index++;
        }
    }

    public class lineDataBlock {
        public short Elevation;
        public int Azimuth;
        public String Longitude;
        public String Latitude;
        public float Vs;
        public float Hs;
        public int Course;
        public int Nv;
        public byte[] CorZ;
        public byte[] UnZ;
        public int[] V;
        public byte[] W;

        public void parse(byte[] b) {
            int index = 0;
            Elevation = BitConverter.toInt16(b, index);
            index += 2;
            Azimuth = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            Longitude = new String(Arrays.copyOfRange(b, index, index + 16));
            index += 16;
            Latitude = new String(Arrays.copyOfRange(b, index, index + 16));
            index += 16;
            Vs = BitConverter.toSingle(b, index);
            index += 4;
            Hs = BitConverter.toSingle(b, index);
            index += 4;
            Course = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            Nv = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            CorZ = new byte[1024];
            System.arraycopy(b, index, CorZ, 0, CorZ.length);
            index += 1024;
            UnZ = new byte[1024];
            System.arraycopy(b, index, UnZ, 0, UnZ.length);
            index += 1024;
            V = new int[1024];
            byte[] bytdata = new byte[1024];
            System.arraycopy(b, index, bytdata, 0, bytdata.length);
            for (int i = 0; i < V.length; i++) {
                V[i] = (0xff & bytdata[i]);
            }
            index += 1024;
            W = new byte[1024];
            System.arraycopy(b, index, W, 0, W.length);
            index += 1024;
        }
    }
    //endregion

}
